package org.ayal.SPT;

/* Copyright 2011 Shai Ayal
 * 
 * This file is part of SPT.
 *
 * SPT is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * SPT is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SPT.  If not, see <http://www.gnu.org/licenses/>.
 */


import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

import org.ayal.SPT.Connection.ConnectionState;
import org.ayal.SPT.ConnectionInfo.InvalidException;
import org.ayal.SPT.util.General;
import org.ayal.SPT.util.SimpleGrep;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.text.method.ScrollingMovementMethod;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.webkit.WebView;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
import android.widget.CompoundButton.OnCheckedChangeListener;

/**
 * @author shaia
 *
 */
public class Main extends Activity {

	/* Intents for automation.
	 * use for "batch" e.g. using the am tool:
	 * am start -n org.ayal.SPT/.Main -a org.ayal.SPT.ACTION_CONNECT
	 */
	
	/** CONNECT - this really only makes sense if when using an unencrypted private key, otherwise it will prompt for a password/passkey */
	public static final String ACTION_CONNECT = "org.ayal.SPT.ACTION_CONNECT";
	/** DISCONNECT - disconnects, but does not stop service */
	public static final String ACTION_DISCONNECT = "org.ayal.SPT.ACTION_DISCONNECT";
	/** STOP - stops service */
	public static final String ACTION_STOP = "org.ayal.SPT.ACTION_STOP";

	private TextView text = null;
	private ToggleButton tb = null;
	private static final String HELP_FILE = "help.html";
	private static final String LICENSE_FILE = "gpl-3.0.html";

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		java.security.Security.insertProviderAt(new org.bouncycastle2.jce.provider.BouncyCastleProvider(), 1);

		setContentView(R.layout.main);
		text = (TextView) findViewById(R.id.main_text);
		text.setMovementMethod(new ScrollingMovementMethod());
		AddText("",true);

		tb = (ToggleButton) findViewById(R.id.main_onoff);
		tb.setEnabled(true);

		final Intent intent = new Intent ();
		final ComponentName cn = new ComponentName(this,Connection.class);
		intent.setComponent(cn);

		try {
			startService(intent);
		}
		catch(Exception e) {
			AddText (e.toString());
		}
	}

	/* (non-Javadoc)
	 * @see android.app.Activity#onResume()
	 */
	@Override
	protected void onResume() {
		super.onResume();
		DisplayStatus();
	}

	/**
	 * Set ConnectionInfo and pass it to the Connection service
	 * @param password
	 * @param passphrase
	 */
	private void DoConnection (String password, String passphrase) {
		final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);

		try {
			ConnectionInfo ci = new  ConnectionInfo(
					prefs.getString("hostid", ""),
					prefs.getString("username", ""),
					General.Str2Int(prefs.getString("port", "22")),
					passphrase,
					password,
					prefs.getString("privatekeyfile", ""),
					prefs.getBoolean("compression", true),
					prefs.getBoolean("remoteaccept", false),
					prefs.getBoolean("localaccept", false),
					General.Str2Int(prefs.getString("dynamic", "0")
					));

			ci.AddForwards(prefs.getString("forwards", ""));
			ci.Validate();
			mBoundService.setConnectionInfo(ci);

		} catch (InvalidException e) {
			Toast.makeText(getApplicationContext(), "Settings Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
			setOnOffChecked(false);
		}
	}
	
	private void connect () {
		//  no use in doing anything unless we are bound ...
		if (mBoundService == null) return;

		final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
		String privatekey = prefs.getString("privatekeyfile", "");

		// do we need a password at all?
		if (privatekey.length()>0) {
			SimpleGrep sg = new SimpleGrep();
			if (!sg.Find(privatekey, "ENCRYPTED")) {
				DoConnection("", "");
				return;
			}
		}

		// We need a password -- lets get it from the user!
		AlertDialog.Builder alert = new AlertDialog.Builder(this);

		if (privatekey.length() == 0) {
			alert.setTitle("Password");
		}
		else {
			alert.setTitle("Passphrase");
		}       

		// Set an EditText view to get user input 
		final EditText input = new EditText(this);
		input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
		alert.setView(input);

		alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int whichButton) {
				String privatekey = prefs.getString("privatekeyfile", "");
				String password = "";
				String passphrase = "";

				if (privatekey.length() == 0) {password = input.getText().toString();}
				else {passphrase = input.getText().toString();}
				
				DoConnection(password, passphrase);
			}
		});

		alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int whichButton) {
				AddText ("User Canceled\n");
				setOnOffChecked(false);
			}
		});

		alert.show();
	}

	private void disconnect () {
		//  no use in doing anything unless we are bound ...
		if (mBoundService == null) return;

		mBoundService.setConnectionInfo(null);
	}
	
	private void exit () {
		disconnect();
		Intent intent = new Intent ();
		ComponentName cn = new ComponentName(this,Connection.class);
		intent.setComponent(cn);
		stopService(intent);
		finish ();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle item selection
		Intent intent = null;
		switch (item.getItemId()) {
		case R.id.settings:
			intent = new Intent(this, hostPreferences.class);
			startActivity(intent);
			return true;
		case R.id.exit:
			exit ();
			return true;
		case R.id.knownhosts:
			intent = new Intent(this, KnownHostsManager.class);
			startActivity(intent);
			return true;
		case R.id.help: 
			ShowHTML ("Help", HELP_FILE);
			return true;
		case R.id.license:
			ShowHTML ("License", LICENSE_FILE);
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	private String convertStreamToString(InputStream is) { 
		return new Scanner(is).useDelimiter("\\A").next();
	}
	
	private void ShowHTML(String title, String filename) {
		AlertDialog.Builder alert = new AlertDialog.Builder(this);
		alert.setTitle(title);
		final WebView wv = new WebView (this);
		AssetManager am = getAssets();
		try {
			wv.loadData(convertStreamToString(am.open(filename)), "text/html", "UTF-8");
			alert.setView(wv);
			alert.show();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void AddText (String txt){
		text.setText (text.getText() + "\n" + txt);
	}

	private void AddText (String txt, boolean clear){
		if (clear == true) {
			text.setText(R.string.main_screen_text);
		}
		AddText (txt);
	}
	
	private void DisplayStatus ()
	{
		if (mBoundService != null) {
			AddText("Bound to service",true);
			AddText("Service state: " + mBoundService.getState().toString());
			AddText("Last Message :");
			AddText("");
			AddText("  "+mBoundService.getLastMessgage());
		}
	}

	/* (non-Javadoc)
	 * @see android.app.Activity#onStart()
	 */
	@Override
	protected void onStart() {
		super.onStart();
		tb.setEnabled(false);
		// bind to service 
		doBindService();
	}


	/* (non-Javadoc)
	 * @see android.app.Activity#onStop()
	 */
	@Override
	protected void onStop() {
		super.onStop();
		doUnbindService();
	}

	private Connection mBoundService = null;
	private Boolean mIsBound;

	private ServiceConnection mConnection = new ServiceConnection() {
		public void onServiceConnected(ComponentName className, IBinder service) {
			// This is called when the connection with the service has been
			// established, giving us the service object we can use to
			// interact with the service.  Because we have bound to a explicit
			// service that we know is running in our own process, we can
			// cast its IBinder to a concrete class and directly access it.
			mBoundService = ((Connection.LocalBinder)service).getService();

			DisplayStatus();
			// Set the correct state for the connection button
			ConnectionState state = mBoundService.getState();
			setOnOffChecked( state != ConnectionState.IDLE);
			tb.setEnabled(true);

			/* parse connection intents -- do the action specified and finish since we don't
			 * want the activity to stay running 
			 */
			String Action= getIntent().getAction();
			if ( Action != null) {
				if (Action.equals(ACTION_CONNECT)) {
					tb.setChecked(true);
					finish();
				}
				else if (Action.equals(ACTION_DISCONNECT)) {
					tb.setChecked(false);
					finish();
				}
				else if (Action.equals(ACTION_STOP)) {
					exit ();
				}
			}
		}

		public void onServiceDisconnected(ComponentName className) {
			// This is called when the connection with the service has been
			// unexpectedly disconnected -- that is, its process crashed.
			// Because it is running in our same process, we should never
			// see this happen.
			mBoundService = null;
		}
	};

	void doBindService() {
		// Establish a connection with the service.  We use an explicit
		// class name because we want a specific service implementation that
		// we know will be running in our own process (and thus won't be
		// supporting component replacement by other applications).
		bindService(new Intent(Main.this, 
				Connection.class), mConnection, Context.BIND_AUTO_CREATE);
		mIsBound = true;
	}

	void doUnbindService() {
		if (mIsBound) {
			// Detach our existing connection.
			unbindService(mConnection);
			mBoundService = null;
			mIsBound = false;
			tb.setEnabled(false);
		}
	}

	/**
	 * set the state of the tb without triggering the callback
	 * @param checked
	 */
	private void setOnOffChecked (Boolean checked) {
		tb.setOnCheckedChangeListener(null);
		tb.setChecked(checked);
		tb.setOnCheckedChangeListener(new ccl());
	}

	public class ccl implements OnCheckedChangeListener {
		public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
			if (isChecked) { connect (); }
			else {  disconnect(); }
		}
	}
	
}
